package com.von.aspect;

import cn.hutool.json.JSONUtil;
import com.von.ann.ThirdLog;
import com.von.mapper.ThirdLogInfoMapper;
import com.von.entity.ThirdLogInfo;
import com.von.utils.ThreadPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;

@Aspect
@Component
@Slf4j
public class ThirdLogAspect {

    private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<Date>("ThreadLocal beginTime");

    ThirdLogInfo logInfo = null;

    @Autowired
    private ThirdLogInfoMapper thirdLogInfoMapper;

    /**
     * 调用三方切点,注解方式
     */
    @Pointcut("@annotation(com.von.ann.ThirdLog)")
    public void thirdAspect() {
    }

    /**
     * 前置通知
     *
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("thirdAspect()")
    public void doBefore(JoinPoint joinPoint) throws ClassNotFoundException {
        logInfo = new ThirdLogInfo();
        Method[] methods = Class.forName(joinPoint.getTarget().getClass().getName()).getMethods();
        for (Method method : methods) {
            if (!method.getName().equals(joinPoint.getSignature().getName())) {
                continue;
            }
            String value = method.getAnnotation(ThirdLog.class).value();
            logInfo.setName(value);
        }
        Object[] args = joinPoint.getArgs();
        logInfo.setRequestParam(JSONUtil.toJsonStr(args));
        //线程绑定变量（该数据只有当前请求的线程可见）
        Date beginTime = new Date();
        beginTimeThreadLocal.set(beginTime);
    }

    /**
     * 异常通知
     *
     * @param throwinfo 异常
     */
    @AfterThrowing(pointcut = "thirdAspect()", throwing = "throwinfo")
    public void throwafter(Throwable throwinfo) {
        long beginTime = beginTimeThreadLocal.get().getTime();
        long endTime = System.currentTimeMillis();
        //请求耗时
        Long logElapsedTime = endTime - beginTime;
        logInfo.setCostTime(logElapsedTime.intValue());
        logInfo.setException(throwinfo.getMessage());
        ThreadPoolUtil.getPool().execute(new ThirdLogAspect.SaveSystemLogThread(logInfo, thirdLogInfoMapper));
    }

    /**
     * 后置通知
     *
     * @param joinPoint 切点
     */
    @AfterReturning(returning = "rvt", pointcut = "thirdAspect()")
    public void after(JoinPoint joinPoint, Object rvt) {
        try {
            long beginTime = beginTimeThreadLocal.get().getTime();
            long endTime = System.currentTimeMillis();
            //请求耗时
            Long logElapsedTime = endTime - beginTime;
            logInfo.setCostTime(logElapsedTime.intValue());
            logInfo.setReturnData(JSONUtil.toJsonStr(rvt));
            ThreadPoolUtil.getPool().execute(new ThirdLogAspect.SaveSystemLogThread(logInfo, thirdLogInfoMapper));
        } catch (Exception e) {
            log.error("AOP后置通知异常", e);
        }
    }

    /**
     * 保存日志至数据库
     */
    private static class SaveSystemLogThread implements Runnable {

        private ThirdLogInfo log;

        private ThirdLogInfoMapper thirdLogInfoMapper;

        public SaveSystemLogThread(ThirdLogInfo esLog, ThirdLogInfoMapper thirdLogInfoMapper) {
            this.log = esLog;
            this.thirdLogInfoMapper = thirdLogInfoMapper;
        }

        @Override
        public void run() {
            thirdLogInfoMapper.insert(log);
        }
    }
}
